#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QTextStream>
#include <sstream>
#include "SceneManager.hpp"
#include "XmlSaxParser.hpp"
#include "3D/Model.hpp"

SceneManager::SceneManager()
{
}

void                    SceneManager::save(std::string const& fileName, Scene& scene)
{
    QDomDocument        document;
    QDomElement         rootElement = document.createElement("scene");

    addToDom(document, rootElement, "name", scene.getName().data());

    Terrain*            terrain = scene.getTerrain();

    if (terrain != NULL)
    {
        QDomElement         terrainElement = document.createElement("terrain");

        addToDom(document, terrainElement, "width", terrain->getWidth());
        addToDom(document, terrainElement, "height", terrain->getHeight());

        QDomElement         verticesElement = document.createElement("vertices");
        DrawableObject*     terrainDrawable = terrain->getDrawable();
        VertexAttributes*   vertices = terrainDrawable->getVertices();
        unsigned int        nbVertices = terrainDrawable->getNbVertices();

        addToDom(document, terrainElement, "nb_vertices", terrainDrawable->getNbVertices());
        addToDom(document, terrainElement, "nb_indexes", terrainDrawable->getNbIndex());

        for (unsigned int itVertices = 0; itVertices < nbVertices; itVertices++)
        {
            QDomElement     vertexElement = document.createElement("vertex");
            addToDom(document, vertexElement, "pos_x", vertices[itVertices].posX);
            addToDom(document, vertexElement, "pos_y", vertices[itVertices].posY);
            addToDom(document, vertexElement, "pos_z", vertices[itVertices].posZ);

            addToDom(document, vertexElement, "col_r", vertices[itVertices].colorR);
            addToDom(document, vertexElement, "col_g", vertices[itVertices].colorG);
            addToDom(document, vertexElement, "col_b", vertices[itVertices].colorB);

            addToDom(document, vertexElement, "norm_x", vertices[itVertices].normX);
            addToDom(document, vertexElement, "norm_y", vertices[itVertices].normY);
            addToDom(document, vertexElement, "norm_z", vertices[itVertices].normZ);

            addToDom(document, vertexElement, "text_x", vertices[itVertices].posX);
            addToDom(document, vertexElement, "text_y", vertices[itVertices].posY);
            addToDom(document, vertexElement, "text_z", vertices[itVertices].posZ);

            addToDom(document, vertexElement, "id_x", vertices[itVertices].idX);
            addToDom(document, vertexElement, "id_y", vertices[itVertices].idY);
            addToDom(document, vertexElement, "id_z", vertices[itVertices].idZ);

            verticesElement.appendChild(vertexElement);
        }
        terrainElement.appendChild(verticesElement);

        unsigned int*   index = terrainDrawable->getIndex();
        unsigned int    nbIndex = terrainDrawable->getNbIndex();
        QDomElement     indexesElement = document.createElement("indexes");

        for (unsigned int itIndex = 0; itIndex < nbIndex; itIndex++)
        {
            addToDom(document, indexesElement, "index", index[itIndex]);
        }
        terrainElement.appendChild(indexesElement);

        Texture*            texture = terrain->getTexture();

        if (texture != NULL)
        {
            QDomElement         textureElement = document.createElement("texture");
            addToDom(document, textureElement, "text_name", texture->getName().data());
            addToDom(document, textureElement, "text_path", texture->getPath().data());
            addToDom(document, textureElement, "text_behaviour", texture->getBehaviour());
            terrainElement.appendChild(textureElement);
        }
        rootElement.appendChild(terrainElement);
    }

    std::list<DrawableObject*>& objects = scene.getObjects();

    if (objects.empty() == false)
    {
        QDomElement                 modelsElement = document.createElement("models");
        std::list<DrawableObject*>::iterator    itBegin = objects.begin();
        std::list<DrawableObject*>::iterator    itEnd = objects.end();

        while (itBegin != itEnd)
        {
            QDomElement     modelElement = document.createElement("model");
            Model*          model = dynamic_cast<Model*>(*itBegin);
            Vec3f&          position = model->getPosition();

            addToDom(document, modelElement, "model_path", model->getPath().data());
            addToDom(document, modelElement, "model_pos_x", position[0]);
            addToDom(document, modelElement, "model_pos_y", position[1]);
            addToDom(document, modelElement, "model_pos_z", position[2]);
            modelsElement.appendChild(modelElement);
            ++itBegin;
        }
        rootElement.appendChild(modelsElement);
    }

    document.appendChild(rootElement);
    writeToFile(document, fileName);
}

void                SceneManager::addToDom(QDomDocument& document, QDomElement& element, QString const& name, QString const& value)
{
    QDomElement     child = document.createElement(name);
    child.appendChild(document.createTextNode(value));
    element.appendChild(child);
}

void                SceneManager::addToDom(QDomDocument& document, QDomElement& element, QString const& name, unsigned int value)
{
    std::stringstream   converter;

    converter << value;
    QDomElement     child = document.createElement(name);
    child.appendChild(document.createTextNode(converter.str().data()));
    element.appendChild(child);
}


void                SceneManager::addToDom(QDomDocument& document, QDomElement& element, QString const& name, int value)
{
    std::stringstream   converter;

    converter << value;
    QDomElement     child = document.createElement(name);
    child.appendChild(document.createTextNode(converter.str().data()));
    element.appendChild(child);
}

void                SceneManager::addToDom(QDomDocument& document, QDomElement& element, QString const& name, float value)
{
    std::stringstream   converter;

    converter << value;
    QDomElement     child = document.createElement(name);
    child.appendChild(document.createTextNode(converter.str().data()));
    element.appendChild(child);
}

void                SceneManager::writeToFile(QDomDocument& document, const std::string &fileName)
{
    QDomNode        xmlNode = document.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
    QFile           file(fileName.data());
    QTextStream     out;

    if (file.open(QIODevice::WriteOnly) == false)
    {
        throw std::exception();
    }
    document.insertBefore(xmlNode, document.firstChild());
    out.setDevice(&file);
    document.save(out, 2);
    file.close();
}

Scene*    SceneManager::load(const std::string &fileName)
{
    XmlSaxParser    parser;

    return parser.readFile(fileName.data());
}
